﻿using UnityEngine;
using System;
using System.Collections;
using System.Linq;
using System.Reflection;
using System.Collections.Generic;
using System.Text;
using UnityExtensions;
using XmlTypes;

namespace CxExtension
{
	public static class TypeUtility
	{
		private static string listSplit = ",";
		private static string parseFunc = "Parse";
		private static BindingFlags defaultFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public |
		BindingFlags.NonPublic | BindingFlags.ExactBinding | BindingFlags.InvokeMethod |
		BindingFlags.FlattenHierarchy | BindingFlags.IgnoreReturn | BindingFlags.IgnoreCase;                      //
		private static Dictionary<Type, Type[]> subTypesMap;
		private static Dictionary<Type, List<MethodInfo>> methodMap;
		private static Dictionary<Type, Dictionary<string, FieldInfo>> fieldInfoMap;
		private static Dictionary<Type, object> defaultEmptyList;
		static TypeUtility()
		{
			subTypesMap = new Dictionary<Type, Type[]>();
			methodMap = new Dictionary<Type, List<MethodInfo>>();
			fieldInfoMap = new Dictionary<Type, Dictionary<string, FieldInfo>>();
			defaultEmptyList = new Dictionary<Type, object>();
		}

		public static object GetDefaultEmptyList(this Type self)
		{
			object value;
			if (defaultEmptyList.TryGetValue(self,out value) == false)
			{
				value = Activator.CreateInstance(self);
				defaultEmptyList[self] = value;
			}

			return value;
		}

		public static FieldInfo GetFieldInfoCache(this Type self, string name)
		{
			return self.GetFieldInfoCache(name, defaultFlags);
		}

		public static Dictionary<string, FieldInfo> GetFieldInfoMapCache(this Type self)
		{
			var tmap = fieldInfoMap.GetOrNewAdd(self);
			return tmap;
		}

		public static FieldInfo GetFieldInfoCache(this Type self, Dictionary<string, FieldInfo> cacheMap, string name, BindingFlags bindingAttr)
		{
			var finfo = cacheMap.GetOrNewAddF(name, it => self.GetField(name, bindingAttr));
			return finfo;
		}

		public static FieldInfo GetFieldInfoCache(this Type self, Dictionary<string, FieldInfo> cacheMap, string name)
		{
			return self.GetFieldInfoCache(cacheMap, name, defaultFlags);
		}
		public static FieldInfo GetFieldInfoCache(this Type self, string name, BindingFlags bindingAttr)
		{
			var tmap = self.GetFieldInfoMapCache();
			var finfo = tmap.GetOrNewAddF(name, it => self.GetField(name, bindingAttr));
			return finfo;
		}

		public static Dictionary<string, FieldInfo> GetFieldInfoCache(this Type self, IList<string> nameList)
		{
			var flags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public |
						BindingFlags.NonPublic | BindingFlags.ExactBinding | BindingFlags.InvokeMethod |
						BindingFlags.FlattenHierarchy | BindingFlags.IgnoreReturn | BindingFlags.IgnoreCase;
			return self.GetFieldInfoCache(nameList, flags);
		}
		public static Dictionary<string, FieldInfo> GetFieldInfoCache(this Type self, IList<string> nameList,
			BindingFlags bindingAttr)
		{
			var dic = new Dictionary<string, FieldInfo>(nameList.Count);
			foreach (var it in nameList)
			{
				var info = self.GetFieldInfoCache(it, bindingAttr);
				if (info == null)
				{
					continue;
				}
				dic.Add(it, info);
			}

			return dic;
		}

		public static string SerializedToString(this Type self, object value)
		{
			if (value == null)
			{
				return string.Empty;
			}
			var propertyType = self;
			if (propertyType == typeof(string))
			{
				return value.ToString();
			}

			if (propertyType.IsEnum)
			{
				return ((int) value).ToString();
			}

			if (propertyType.IsGenericType)
			{
				var gTypeDef = propertyType.GetGenericTypeDefinition();
				if (gTypeDef == typeof(List<>))
				{
					var genericType = propertyType.GetGenericArguments()[0];
					var list = value as IList;
					var build = new StringBuilder();
					foreach (var it in list)
					{
						var str = genericType.SerializedToString(it);
						build.Append(str);
						build.Append(listSplit);
					}

					if (list.Count > 0)
					{
						build.RemoveLast(listSplit.Length);
					}
					return build.ToString();
				}
			}

			throw new NotImplementedException("not NotImplementedType Serialized type:" + self.Name);
		}

		public static object ParseValueByString(this Type self, string value)
		{
			var propertyType = self;
			#region base type
			if (propertyType == typeof(string))
			{
				return value;
			}

			if (propertyType.IsEnum)
			{
				var ovalue = Enum.Parse(propertyType, value);
				if (Enum.IsDefined(propertyType, ovalue))
				{
					return ovalue;
				}

				Debug.LogErrorFormat("枚举{0} 没有值{1}", propertyType.FullName, value);
				return null;
			}
			#endregion
			#region list<>

			if (propertyType.IsGenericType)
			{
				var gTypeDef = propertyType.GetGenericTypeDefinition();
				if (gTypeDef == typeof(List<>))
				{
					var genericType = propertyType.GetGenericArguments()[0];

					var sp = value.Split(listSplit);
					if (sp.Length > 0)
					{
						var lv = Activator.CreateInstance(propertyType);
						var list = lv as IList;
						foreach (var it in sp)
						{
							var fit = it.Trim();
							if (fit.IsNullOrEmpty() || fit == "0") continue;

							var v = genericType.ParseValueByString(it);
							list.Add(v);
						}
						return lv;
					}
					else
					{
						return propertyType.GetDefaultEmptyList();
					}

				}

				return null;
			}
			#endregion
			#region other type
			else
			{

				var parseMethod = propertyType.ParseMethod();
				if (parseMethod == null)
				{
					Debug.LogErrorFormat("no method {0}", parseFunc);
					return null;
				}

				object[] param = { value };
				var valueO = parseMethod.Invoke(parseMethod, param);
				//Debug.Log (string.Format ("set the value [{0}] is {1}", d.Key, value));
				return valueO;
			}
			#endregion
		}

		/// <summary>
		/// 获取所有派生类的名字数组
		/// </summary>
		/// <param name="self"></param>
		/// <returns></returns>
		public static string[] GetSubTypeNames(this Type self)
		{
			return self.GetSubTypes().Select(x => x.Name).ToArray();
		}


		public static MethodInfo GetMethodByCache(this Type self, string methodName, BindingFlags bindingAttr, Type[] paramTypes = null)
		{
			var lst = methodMap.GetOrNewAdd(self);
			MethodInfo info = null;
			var has = lst.FindA(it => it.Name == methodName, out info);
			if (has)
			{
				return info;
			}

			var methodList = self.GetMethods(bindingAttr);
			info = paramTypes == null
				? self.GetMethod(methodName, bindingAttr)
				: self.GetMethod(methodName, bindingAttr, null, paramTypes, null);
			if (info == null)
			{
				Debug.LogErrorFormat("not find method:{0} in type:{1}", methodName, self.FullName);
				return null;
			}

			lst.Add(info);
			return info;
		}

		public static MethodInfo GetMethodByCache(this Type self, string methodName, Type[] paramTypes = null)
		{
			var flags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public |
					 BindingFlags.NonPublic | BindingFlags.ExactBinding | BindingFlags.InvokeMethod |
						BindingFlags.FlattenHierarchy | BindingFlags.IgnoreReturn | BindingFlags.IgnoreCase;
			return self.GetMethodByCache(methodName, flags, paramTypes);
		}

		/// <summary>
		/// 获取某个名字的派生类
		/// </summary>
		/// <param name="self"></param>
		/// <param name="name"></param>
		/// <returns></returns>
		public static Type GetSubTypeByName(this Type self, string name)
		{
			var types = self.GetSubTypes();
			var type = types.FindA(item => item.Name == name);
			if (type == null)
			{
				Debug.LogWarningFormat("not find the sub class[{0}] in [{1}]", name, self.Name);
			}
			return type;
		}
		/// <summary>
		/// 取得某个名字的派生类实例
		/// </summary>
		/// <param name="self"></param>
		/// <param name="name"></param>
		/// <returns></returns>
		public static object GetSubInstanceByName(this Type self, string name)
		{
			var tp = self.GetSubTypeByName(name);
			object v = null;
			if (tp != null)
			{
				v = Activator.CreateInstance(tp);
			}
			return v;
		}
		/// <summary>
		/// 获取唯一的名字 去掉了命名空间 内置类用加号相连
		/// </summary>
		/// <param name="self"></param>
		/// <returns></returns>
		public static string GetSoleName(this Type self)
		{
			var n = self.FullName.Substring(self.Namespace.Length + 1);
			return n;
		}
		/// <summary>
		/// 取得指定枚举类型的某个值的int值
		/// </summary>
		/// <param name="self"></param>
		/// <param name="enumname"></param>
		/// <returns></returns>
		public static int GetValueByName(this Type self, string enumname)
		{
			var u = 0;
			try
			{
				u = (int)Enum.Parse(self, enumname, true);
			}
			catch (Exception)
			{
				Debug.LogErrorFormat("node find {0} in class {1}", enumname, self.FullName);
				throw;
			}
			return u;
		}
		/// <summary>
		/// 取得所有派生类型
		/// </summary>
		/// <param name="self"></param>
		/// <returns></returns>
		public static Type[] GetSubTypes(this Type self)
		{
			Type[] types;
			if (!subTypesMap.TryGetValue(self, out types))
			{
#if true
				var typeList = AppDomain.CurrentDomain.GetAssemblies().SelectMany(assembly =>
				{
					Type[] types1 = null;
					try
					{
						types1 = assembly.GetTypes();
					}
					catch (Exception e)
					{
						//System.Console.WriteLine(e);
						//throw;
					}
					if (types1 == null)
					{
						types1 = new Type[] { };
					}
					return types1;
				});
#else
				var typeList = self.Assembly.GetTypes();
#endif
				var typesI = typeList.Where(type => type.IsSubclassOf(self));
				types = typesI.ToArray();
				subTypesMap[self] = types;
			}
			return types;
		}
		/// <summary>
		/// 获取指定名字的成员的类型
		/// </summary>
		/// <param name="self"></param>
		/// <param name="name"></param>
		/// <returns></returns>
		public static Type GetMemberType(this Type self, string name)
		{
			var fieldInfo = self.GetField(name);
			if (fieldInfo != null)
			{
				return fieldInfo.FieldType;
			}
			var propertyInfo = self.GetProperty(name);
			if (propertyInfo != null)
			{
				return propertyInfo.PropertyType;
			}
			return null;
		}
		/// <summary>
		/// 取得某个名字的类型
		/// </summary>
		/// <param name="name"></param>
		/// <returns></returns>
		public static Type GetType(string name)
		{
			var type = AppDomainCache.GetType(name);
			return type;
		}

		public static Type[] GetTypeByName(string className)
		{
			var returnVal = new List<Type>();
			var asset = AppDomain.CurrentDomain.GetAssemblies();
			foreach (var a in asset)
			{
				try
				{
					var assemblyTypes = a.GetTypes();
					if (assemblyTypes == null)
					{
						continue;
					}
					foreach (var it in assemblyTypes)
					{
						if (it == null)
						{
							continue;
						}

						if (it.Name == className)
						{
							returnVal.Add(it);
						}
					}
				}

				catch (Exception e)
				{
				}
			}

			return returnVal.ToArray();
		}
	}
}